package com.swt.gap.service.impl;

import com.swt.gap.common.NodeTypeEnum;
import com.swt.gap.common.constant.GraphDbConstant;
import com.swt.gap.common.graph.GraphNeo4jVO;
import com.swt.gap.common.graph.GraphProcessor;
import com.swt.gap.common.graph.RelEventNode;
import com.swt.gap.config.ServerConfig;
import com.swt.gap.model.vo.EventNodeRelMediaVO;
import com.swt.gap.model.vo.EventRelDocVO;
import com.swt.gap.model.vo.graph.NodeDocVO;
import com.swt.gap.service.EventTimingAnalysisService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 请填写说明
 *
 * @author sx
 * @date 2020/6/19 18:21
 */
@Service
public class EventTimingAnalysisServiceImpl implements EventTimingAnalysisService {

    private final GraphProcessor graphProcessor;
    private final ServerConfig serverConfig;
    private final GraphNodeServiceImpl graphNodeService;

    public EventTimingAnalysisServiceImpl(GraphProcessor graphProcessor, ServerConfig serverConfig,
                                          GraphNodeServiceImpl graphNodeService) {
        this.graphProcessor = graphProcessor;
        this.serverConfig = serverConfig;
        this.graphNodeService = graphNodeService;

    }
    @Override
    public List<GraphNeo4jVO.Node> listEventNodeByName(Integer taskId, Integer pageNum, Integer pageSize, String keyword) {
        String cypher = "MATCH (n:`" + NodeTypeEnum.EVENT.getName() + "`) WHERE n.taskId=" + taskId +
                " and n." + GraphDbConstant.PROPERTY_KEY_NAME + " =~ '.*" + keyword.trim() + ".*' RETURN n ORDER BY n.name SKIP " + ((pageNum -1) * pageSize)+ " LIMIT " + pageSize;

        GraphNeo4jVO graph = graphProcessor.getNodeByCypher(cypher);
        return graph.getNodes();
    }

    @Override
    public List<GraphNeo4jVO.Node> listSameLabelEventNode(Integer taskId, String nodeId, String labels,
                                                          Integer pageNum, Integer pageSize) {
        StringBuilder sbCypher = new StringBuilder();
        sbCypher.append("MATCH (n");
        String[] labelArr = labels.split(",");
        for (String label : labelArr) {
            sbCypher.append(":`").append(label).append("`");
        }
        sbCypher.append(") WHERE n.taskId=")
                .append(taskId)
                .append(" and n.id <> '")
                .append(nodeId)
                .append("' RETURN n ORDER BY n.name SKIP ")
                .append((pageNum -1) * pageSize)
                .append(" LIMIT ")
                .append(pageSize);
        GraphNeo4jVO graph = graphProcessor.getNodeByCypher(sbCypher.toString());
        return graph.getNodes();
    }

    @Override
    public List<RelEventNode> listSameRelEventNode(Integer taskId, String[] nodeIds, Integer pageNum, Integer pageSize) {

        StringBuilder sbCypher = new StringBuilder();

        StringBuilder sbIds = new StringBuilder();
        sbIds.append("[");
        for (int i = 0; i < nodeIds.length; i++) {
            sbIds.append("'").append(nodeIds[i]).append("'");
            if (i < nodeIds.length - 1) {
                sbIds.append(",");
            }
        }
        sbIds.append("]");
        sbCypher.append("MATCH p=(s1)--(m)--(s2) WHERE s1<>s2 and s1.id IN ")
                .append(sbIds)
                .append(" AND s2.id IN ")
                .append(sbIds)
                .append("AND m.taskId = ")
                .append(taskId)
                .append(" WITH m, count(distinct(s1)) as num MATCH (m)-[]-(n:`")
                .append(NodeTypeEnum.EVENT.getName())
                .append("`) WHERE NOT n.id IN ")
                .append(sbIds)
                .append("AND n.taskId=")
                .append(taskId)
                .append(" RETURN n, m  ORDER BY num desc, n.name SKIP ")
                .append(((pageNum -1) * pageSize))
                .append(" LIMIT ")
                .append(pageSize);

        return graphProcessor.getRelEventNodeByCypher(sbCypher.toString());
    }

    @Override
    public List<RelEventNode> listRelEventNode(Integer taskId, String nodeId, Integer pageNum, Integer pageSize) {
        List<RelEventNode> retValue = new ArrayList<>();
        StringBuilder sbCypher = new StringBuilder();
        sbCypher.append("MATCH p=(s1)--(m)--(n:`")
                .append(NodeTypeEnum.EVENT.getName())
                .append("`) WHERE s1.id = '")
                .append(nodeId)
                .append("' AND m.taskId = ")
                .append(taskId)
                .append(" AND n.taskId = ")
                .append(taskId)
                .append(" RETURN distinct(n) ORDER BY n.name SKIP ")
                .append(((pageNum -1) * pageSize))
                .append(" LIMIT ")
                .append(pageSize);
        GraphNeo4jVO graph = graphProcessor.getNodeByCypher(sbCypher.toString());

        graph.getNodes().forEach(node -> {
            RelEventNode relEventNode = new RelEventNode();
            BeanUtils.copyProperties(node, relEventNode);

            // 可能同一个事件，由不同的中间节点引出
            StringBuilder sbCypher2 = new StringBuilder();
            sbCypher2.append("MATCH p=(s1)--(n)--(t:`")
                    .append(NodeTypeEnum.EVENT.getName())
                    .append("`) WHERE s1.id = '")
                    .append(nodeId)
                    .append("' AND n.taskId = ")
                    .append(taskId)
                    .append(" AND t.taskId = ")
                    .append(taskId)
                    .append(" AND t.id = '")
                    .append(relEventNode.getId())
                    .append("' RETURN n ");
            GraphNeo4jVO graph2 = graphProcessor.getNodeByCypher(sbCypher2.toString());
            List<String> relNodeNames = new ArrayList<>();
            graph2.getNodes().forEach(node2 -> {
                relNodeNames.add("同" + node2.getLabels().get(1) + "-" + node2.getName());
            });
            relEventNode.setRelNodeNames(relNodeNames);

            retValue.add(relEventNode);
        });


        return retValue;

        /*// 可能同一个事件，由不同的中间节点引出，对这种情况进行合并
        Map<String, RelEventNode> mapRelEventNode = new HashMap<>();
        relEventNodes.forEach(relEventNode -> {
            mapRelEventNode.merge(relEventNode.getId(), relEventNode, (oldNode, newNode) -> {
                List<String> relNodeNames  = oldNode.getRelNodeNames();
                relNodeNames.addAll(newNode.getRelNodeNames());
                oldNode.setRelNodeNames(relNodeNames);
                return oldNode;
            });
        });
        List<RelEventNode> retValue = new ArrayList<>();
        mapRelEventNode.forEach((k,v) -> {
            retValue.add(v);
        });
        return retValue;*/
    }

    @Override
    public List<EventRelDocVO> eventRelMedia(String[] nodeIds, Integer taskId) {
        List<EventRelDocVO> retValue = new ArrayList<>();

        for(String nodeId : nodeIds) {
            List<NodeDocVO> nodeDocs = new ArrayList<>();
            String label = NodeTypeEnum.DOC.getName();
            String labelCypher = ":" + label;

            String cypher = "Match (s{taskId:" + taskId + ", id:'" + nodeIds[0] + "'})-[]-(n"
                    + labelCypher + "{taskId:" + taskId + "})   RETURN n";
            GraphNeo4jVO results = graphProcessor.getNodeByCypher(cypher);
            results.getNodes().forEach(node -> {
                NodeDocVO nodeDoc = graphNodeService.transferNodeToNodeDocVO(node.getProperties());
                if (null != nodeDoc.getDocType()) {
                    nodeDocs.add(nodeDoc);
                }
            });

            EventRelDocVO relDocVO = new EventRelDocVO(nodeId, nodeDocs);
            retValue.add(relDocVO);
        }

        return retValue;
    }

    @Override
    public List<EventNodeRelMediaVO> eventRelMedia2(List<GraphNeo4jVO.Node> nodes, Integer taskId) {
        List<EventNodeRelMediaVO> retValue = new ArrayList<>();

        nodes.forEach(node -> {
            EventNodeRelMediaVO retNode = new EventNodeRelMediaVO();
            BeanUtils.copyProperties(node, retNode);
            List<NodeDocVO> nodeDocs = new ArrayList<>();
            String label = NodeTypeEnum.DOC.getName();
            String labelCypher = ":" + label;

            String cypher = "Match (s{taskId:" + taskId + ", id:'" + node.getId() + "'})-[]-(n"
                    + labelCypher + "{taskId:" + taskId + "})   RETURN n";
            GraphNeo4jVO results = graphProcessor.getNodeByCypher(cypher);
            results.getNodes().forEach(resNode -> {
                NodeDocVO nodeDoc = graphNodeService.transferNodeToNodeDocVO(resNode.getProperties());
                if (null != nodeDoc.getDocType()) {
                    nodeDocs.add(nodeDoc);
                }
            });
            retNode.setRelDocNodes(nodeDocs);
            retValue.add(retNode);
        });


        return retValue;
    }

}
